﻿// Decompiled with JetBrains decompiler
// Type: Netick.Serialization
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

#nullable disable
namespace Netick;

internal static class Serialization
{
  internal static void SendClientPackets(NetickEngine engine, bool shouldSend)
  {
    engine._gameEngine.OnBeforeSend((NetworkConnection) engine.Client.ConnectedServer);
    ++engine._snapshotTick;
    engine._client.ConnectedServer.WritePackets((SerializationCache) null);
    engine._client.ConnectedServer.SendPackets(shouldSend);
    engine._peer.Transport.ForceUpdate();
  }

  internal static void SendServerPackets(NetickEngine engine, bool shouldSend)
  {
    engine._internalReplicator.Entity.Process();
    engine._mainWriter.Cache.Clear();
    for (int index = 0; index < engine.Server.ConnectedClients.Count; ++index)
      engine._gameEngine.OnBeforeSend((NetworkConnection) engine.Server.ConnectedClients[index]);
    ++engine._snapshotTick;
    if (engine._simulateClientsMode)
    {
      Serialization.SimulateSendServerPackets(engine, shouldSend);
    }
    else
    {
      List<ServerConnection> connectedClients = engine.Server.ConnectedClients;
      bool multithreadingEnabled = engine.IsMultithreadingEnabled;
      List<ServerConnection> clients = connectedClients;
      SerializationCache cache = engine._mainWriter.Cache;
      if (multithreadingEnabled)
      {
        for (int index = 0; index < clients.Count; ++index)
          clients[index].WritePacketsFirstPass();
        engine._mainWriter.Write(clients, engine._workers);
      }
      else
      {
        for (int index = 0; index < clients.Count; ++index)
          clients[index].WritePackets(cache);
      }
      for (int index = 0; index < clients.Count; ++index)
        clients[index].SendPackets(shouldSend);
      engine._peer.Transport.ForceUpdate();
    }
  }

  private static void SimulateSendServerPackets(NetickEngine engine, bool shouldSend)
  {
    List<ServerConnection> clients = engine.Server.Clients;
    int num = engine.IsMultithreadingEnabled ? 1 : 0;
    SerializationCache cache = engine._mainWriter.Cache;
    if (num != 0)
    {
      for (int index = 0; index < clients.Count; ++index)
        clients[index].WritePacketsFirstPass();
      engine._mainWriter.Write(clients, engine._workers);
    }
    else
    {
      for (int index = 0; index < clients.Count; ++index)
        clients[index].WritePackets(cache);
    }
    for (int index = 0; index < clients.Count; ++index)
      clients[index].SendPackets(shouldSend);
    engine._peer.Transport.ForceUpdate();
  }

  internal static unsafe bool Write(
    NetickEngine engine,
    SerializationCache ch,
    ServerConnection conn,
    BitBuffer buffer)
  {
    Entity[] entitiesWorldIndexArray = engine._sim.EntitiesWorldIndexArray;
    InterestManagement im = engine._IM;
    bool flag1 = (!engine._config.EnableNarrowphaseFiltering || !engine._config.EnableInterestManagement) && engine.FastSerialization && !engine.IsARM32Bit;
    int length = im.AllGroups.Length;
    Tick simulationTick = engine._simulationTick;
    Tick snapshotTick = engine._snapshotTick;
    int index1 = conn.Index;
    int mtuBits = conn.MaxOutgoingDataPerTickInBytes * 8;
    Packet packetRecord = conn.PacketRecords[snapshotTick % conn.PacketRecords.Length];
    Tick tick = simulationTick - conn.BasisTickCur >= engine.SnapshotCutOff || !conn.BasisTickCur.IsValid ? Tick.InvalidTick : conn.BasisTickCur;
    int num1 = tick.IsValid ? tick % engine._srvSim.Snapshots.Length : -1;
    packetRecord.Clear();
    buffer.AddInt(conn.Index);
    buffer.AddInt(simulationTick.Value);
    buffer.AddInt(tick.Value);
    int writePos1 = buffer.WritePos;
    buffer.Add(1, 0U);
    int writePos2 = buffer.WritePos;
    buffer.Add(1, 0U);
    buffer.AddBool(conn.SimulationData.ServerReady);
    int num2 = 0;
    float num3 = (float) conn.PacketReceiveDelta.StdDeviation * 1000f;
    float num4 = (float) conn.PacketReceiveDelta.Average * 1000f;
    NetickUtils.SetFloat(&num2, num3, 100f);
    buffer.AddInt(num2);
    NetickUtils.SetFloat(&num2, num4, 100f);
    buffer.AddInt(num2);
    buffer.AddUInt((uint) (1.0 / (double) engine._profiler.ServerTickTime.Latest));
    if (engine.Config.EnableInterestManagement)
      Serialization.WriteIMList(conn, buffer);
    int writePos3 = buffer.WritePos;
    buffer.Add(1, 0U);
    int writePos4 = buffer.WritePos;
    buffer.Add(20, 0U);
    bool flag2 = false;
    bool didWriteAny = false;
    uint num5 = 0;
    bool flag3 = Serialization.WriteMeta(buffer, packetRecord, mtuBits, index1, engine._internalReplicator.Entity, ref didWriteAny);
    if (flag3)
    {
      WriteData wd = new WriteData()
      {
        ClientDCTick = conn.BasisTickCur.Value,
        Client = index1,
        Buffer = buffer,
        MTUBits = mtuBits - 240 /*0xF0*/,
        SnapshotIndex = num1
      };
      bool flag4 = true;
      uint numOfWrittenObjects = 0;
      flag2 = !flag1 ? flag4 & Serialization.WriteGroupNPF(conn, ref wd, length, im.GlobalGroup, entitiesWorldIndexArray, packetRecord, ch, mtuBits, index1, snapshotTick.Value, simulationTick.Value, ref numOfWrittenObjects) : flag4 & Serialization.WriteGroup(conn, ref wd, length, im.GlobalGroup, entitiesWorldIndexArray, packetRecord, ch, mtuBits, index1, snapshotTick.Value, simulationTick.Value, ref numOfWrittenObjects);
      if (numOfWrittenObjects > 0U)
        ++num5;
      if (engine.Config.EnableInterestManagement)
      {
        foreach (InterestManagementGroup interestGroup in conn._interestGroups)
        {
          if (mtuBits < buffer.WritePos + 240 /*0xF0*/)
          {
            flag2 = false;
            break;
          }
          if (flag1)
            flag2 &= Serialization.WriteGroup(conn, ref wd, length, interestGroup, entitiesWorldIndexArray, packetRecord, ch, mtuBits, index1, snapshotTick.Value, simulationTick.Value, ref numOfWrittenObjects);
          else
            flag2 &= Serialization.WriteGroupNPF(conn, ref wd, length, interestGroup, entitiesWorldIndexArray, packetRecord, ch, mtuBits, index1, snapshotTick.Value, simulationTick.Value, ref numOfWrittenObjects);
          if (numOfWrittenObjects > 0U)
            ++num5;
        }
      }
      if (!flag2)
      {
        int client = wd.Client;
        ObjectRecord* ptr = (ObjectRecord*) packetRecord.Entities.Ptr;
        for (int index2 = 0; index2 < packetRecord.Count; ++index2)
        {
          if (ptr[index2].InputSourceId == client)
            ptr[index2].EntityId = -1;
        }
      }
    }
    bool flag5 = flag2 & flag3;
    if (!flag5 && tick.IsValid)
      conn.BasisTickCur = Tick.InvalidTick;
    conn.DidWriteMetaLastTick = flag3;
    buffer.Insert(writePos1, 1, flag3 ? 1U : 0U);
    buffer.Insert(writePos2, 1, didWriteAny ? 1U : 0U);
    buffer.Insert(writePos3, 1, flag5 ? 1U : 0U);
    buffer.Insert(writePos4, 20, num5);
    conn._sentUpdatesTimes[snapshotTick % engine._config.MaxSnapshots] = engine._timer.Time;
    return flag5;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  private static void WriteIMList(ServerConnection conn, BitBuffer buffer)
  {
    buffer.AddUInt4((uint) conn._interestGroups.Count);
    int num = 0;
    foreach (InterestManagementGroup interestGroup in conn._interestGroups)
    {
      buffer.AddInt(interestGroup.Index - num);
      num = interestGroup.Index;
    }
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  private static bool WriteMeta(
    BitBuffer buffer,
    Packet pkt,
    int mtuBits,
    int clientIndex,
    Entity world,
    ref bool didWriteAny)
  {
    int tick = 0;
    WriteData w = new WriteData()
    {
      ClientDCTick = -1,
      Client = clientIndex,
      Buffer = buffer,
      MTUBits = mtuBits - 240 /*0xF0*/,
      SnapshotIndex = -1,
      LastNode = world.GetClientNodeToWrite(clientIndex)
    };
    if (mtuBits < buffer.Length() + 240 /*0xF0*/)
    {
      didWriteAny = false;
      return false;
    }
    didWriteAny = true;
    int num = world.WP(ref w) ? 1 : 0;
    pkt.WorldRecord = new ObjectRecord(-1, -1, false, w.WrittenRangeEnd, tick, -1);
    return num != 0;
  }

  private static unsafe bool WriteGroup(
    ServerConnection conn,
    ref WriteData wd,
    int totalGroupsCount,
    InterestManagementGroup imGroup,
    Entity[] objects,
    Packet pkt,
    SerializationCache ch,
    int mtuBits,
    int clientIndex,
    int snapshotTick,
    int simTick,
    ref uint numOfWrittenObjects)
  {
    numOfWrittenObjects = 0U;
    Entity entity1 = imGroup.Entity;
    Handle handle = entity1.K[clientIndex];
    int n = entity1.C[entity1.K[clientIndex].H].N;
    long i = entity1.C[handle.H].I;
    int index = n;
    int snapshotTick1 = entity1.K[wd.Client].SnapshotTick;
    bool flag1 = snapshotTick1 != -1 && snapshotTick1 == wd.ClientDCTick && wd.SnapshotIndex != -1;
    if (n == -1)
      return true;
    NativeArray<int> idToWorldIndex = imGroup._idToWorldIndex;
    Entity entity2 = objects[idToWorldIndex[entity1.C[n].Index]];
    bool flag2 = true;
    wd.LastNode = entity2.GetClientNodeToWrite(clientIndex);
    int tickOffset = simTick - wd.ClientDCTick;
    int writePos1 = wd.Buffer.WritePos;
    wd.Buffer.Add(20, 0U);
    wd.Buffer.AlignWritePosToByte();
    int writePos2 = wd.Buffer.WritePos;
    int lastEntityId = 0;
    SerializationCache.CacheElement cachedWritten1;
    long rangeEnd;
    if (flag1 && ch.TryGetData(ch.BitBuffersOffset + imGroup.Index, tickOffset, out cachedWritten1) && mtuBits > wd.Buffer.WritePos + cachedWritten1.Count1 + 240 /*0xF0*/)
    {
      ch.PerformBitBufferCopy(ref wd, cachedWritten1);
      SerializationCache.CacheElement cachedWritten2;
      ch.TryGetData(ch.PacketsOffset + imGroup.Index, tickOffset, out cachedWritten2);
      ch.PerformPacketCopy(pkt, cachedWritten2);
      lastEntityId = cachedWritten1.LastEntityId;
      flag2 = true;
      numOfWrittenObjects = (uint) cachedWritten2.Count2;
      rangeEnd = cachedWritten2.LastWrittenRange;
    }
    else
    {
      int count = pkt.Count;
      int num1 = 0;
      long offset = ch.Offset;
      while (n != -1)
      {
        if (mtuBits < wd.Buffer.WritePos + 240 /*0xF0*/)
        {
          flag2 = false;
          break;
        }
        int snapshotTick2 = entity2.K[wd.Client].SnapshotTick;
        int num2 = snapshotTick2 == -1 || snapshotTick2 != wd.ClientDCTick ? 0 : (wd.SnapshotIndex != -1 ? 1 : 0);
        bool flag3 = snapshotTick2 != -1;
        int num3 = flag1 ? 1 : 0;
        bool writeDelta = (num2 & num3) != 0;
        if (flag1 && !flag3)
        {
          ch.ExpandIfRequired();
          *(int*) ((IntPtr) ch.Ptr + (IntPtr) (ch.Offset * 4L)) = entity2.WorldIndex;
          ++ch.Offset;
          ++num1;
          index = n;
          n = entity1.C[n].N;
          entity2 = n != -1 ? objects[idToWorldIndex[entity1.C[n].Index]] : (Entity) null;
        }
        else
        {
          wd.Buffer.AddInt4(entity2.RawId - lastEntityId);
          lastEntityId = entity2.RawId;
          int writePos3 = wd.Buffer.WritePos;
          bool flag4 = entity2._inputSourceIndex == wd.Client;
          wd.IsInputSource = !flag1 && flag4;
          wd.LastNode = entity2.GetClientNodeToWrite(clientIndex);
          flag2 = entity2.WP(ref wd, writeDelta);
          if (flag1 & flag4 && !flag2)
          {
            wd.Buffer.WritePos = writePos3;
            wd.IsInputSource = true;
            wd.LastNode = entity2.GetClientNodeToWrite(clientIndex);
            flag2 = entity2.WP(ref wd, writeDelta);
          }
          pkt.AddEntity(new ObjectRecord(entity2.RawId, entity2.WorldIndex, false, wd.WrittenRangeEnd, simTick, entity2.InternalInputSourceIndex));
          ++numOfWrittenObjects;
          index = n;
          if (flag2)
          {
            n = entity1.C[n].N;
            entity2 = n != -1 ? objects[idToWorldIndex[entity1.C[n].Index]] : (Entity) null;
          }
          else
            break;
        }
      }
      rangeEnd = entity1.C[index].I;
      if (flag2 & flag1)
      {
        if (num1 > 0)
          ch.AddEntityData(ch.FreshObjectsOffset + imGroup.Index, tickOffset, new SerializationCache.CacheElement()
          {
            Count1 = num1,
            PtrOffset = offset,
            HasData = 1
          });
        int count1_1 = pkt.Count - count;
        int count1_2 = wd.Buffer.WritePos - writePos2;
        int num4 = writePos2 >> 3;
        int byteSize = (count1_2 >> 3) + 1;
        ch.Add(rangeEnd, lastEntityId, ch.BitBuffersOffset + imGroup.Index, count1_2, -1, tickOffset, (void*) ((IntPtr) wd.Buffer._chunks + num4), byteSize);
        ch.Add(rangeEnd, lastEntityId, ch.PacketsOffset + imGroup.Index, count1_1, (int) numOfWrittenObjects, tickOffset, (void*) (pkt.Entities.GetPtr() + count), count1_1 * sizeof (ObjectRecord));
      }
      else if (!flag2 & flag1)
        ch.Offset = offset;
    }
    if (flag1 & flag2)
    {
      flag2 = Serialization.WriteInputSourceAndFreshObjects(conn, imGroup, objects, tickOffset, simTick, ref numOfWrittenObjects, ref lastEntityId, ref wd, pkt, ch, mtuBits);
      if (flag2)
        pkt.AddEntity(new ObjectRecord(imGroup.Index, -1, true, rangeEnd, simTick, -1));
    }
    else
      pkt.AddEntity(new ObjectRecord(imGroup.Index, -1, true, rangeEnd, simTick, -1));
    wd.Buffer.Insert(writePos1, 20, numOfWrittenObjects);
    return flag2;
  }

  private static unsafe bool WriteInputSourceAndFreshObjects(
    ServerConnection con,
    InterestManagementGroup group,
    Entity[] objs,
    int tickOffset,
    int simTick,
    ref uint numOfWrittenObjects,
    ref int lastEntityId,
    ref WriteData wd,
    Packet pkt,
    SerializationCache ch,
    int mtuBits)
  {
    List<Entity> controlledEntities = con.ControlledEntities;
    bool flag = true;
    SerializationCache.CacheElement cachedWritten;
    if (ch.TryGetData(ch.FreshObjectsOffset + group.Index, tickOffset, out cachedWritten))
    {
      int count1 = cachedWritten.Count1;
      int* numPtr = (int*) ((IntPtr) ch.Ptr + (IntPtr) (cachedWritten.PtrOffset * 4L));
      for (int index = 0; index < count1; ++index)
      {
        Entity entity = objs[numPtr[index]];
        if (mtuBits < wd.Buffer.WritePos + 240 /*0xF0*/)
        {
          flag = false;
          break;
        }
        wd.IsInputSource = entity._inputSourceIndex == wd.Client;
        wd.LastNode = entity.GetClientNodeToWrite(con.Index);
        wd.Buffer.AddInt4(entity.RawId - lastEntityId);
        flag = entity.WP(ref wd);
        pkt.AddEntity(new ObjectRecord(entity.RawId, entity.WorldIndex, false, wd.WrittenRangeEnd, simTick, entity.InternalInputSourceIndex));
        ++numOfWrittenObjects;
        lastEntityId = entity.RawId;
        if (!flag)
          break;
      }
    }
    if (!flag)
      return false;
    for (int index = 0; index < controlledEntities.Count; ++index)
    {
      Entity entity = controlledEntities[index];
      if (entity.InternalInterestGroup == group)
      {
        if (mtuBits < wd.Buffer.WritePos + 240 /*0xF0*/)
        {
          flag = false;
          break;
        }
        wd.LastNode = entity.GetClientNodeToWrite(con.Index);
        wd.Buffer.AddInt4(entity.RawId - lastEntityId);
        int snapshotTick = entity.K[wd.Client].SnapshotTick;
        bool writeDelta = snapshotTick != -1 && snapshotTick == wd.ClientDCTick && wd.SnapshotIndex != -1;
        flag = entity.WPInputOnly(ref wd, writeDelta);
        ++numOfWrittenObjects;
        lastEntityId = entity.RawId;
        if (!flag)
          break;
      }
    }
    return flag;
  }

  private static unsafe bool WriteGroupNPF(
    ServerConnection conn,
    ref WriteData wd,
    int groupsCount,
    InterestManagementGroup imGroup,
    Entity[] objects,
    Packet pkt,
    SerializationCache ch,
    int mtuBits,
    int clientIndex,
    int snapshotTick,
    int simTick,
    ref uint numOfWrittenObjects)
  {
    numOfWrittenObjects = 0U;
    Entity entity1 = imGroup.Entity;
    Handle handle = entity1.K[clientIndex];
    int n = entity1.C[entity1.K[clientIndex].H].N;
    if (n == -1)
      return true;
    long i1 = entity1.C[handle.H].I;
    int index1 = n;
    NativeArray<int> idToWorldIndex = imGroup._idToWorldIndex;
    int index2 = entity1.C[n].Index;
    int index3 = idToWorldIndex[index2];
    Entity entity2 = objects[index3];
    int num1 = 0;
    bool flag1 = false;
    wd.LastNode = entity2.GetClientNodeToWrite(clientIndex);
    int writePos = wd.Buffer.WritePos;
    wd.Buffer.Add(20, 0U);
    wd.Buffer.AlignWritePosToByte();
    while (n != -1)
    {
      if (mtuBits < wd.Buffer.WritePos + 240 /*0xF0*/)
      {
        flag1 = false;
        break;
      }
      if (entity2.NarrowInterests != null)
      {
        bool flag2 = entity2.NarrowInterests[clientIndex].ChangeTick == int.MaxValue;
        bool flag3 = entity2.NarrowInterests[clientIndex].AckTick >= entity2.NarrowInterests[clientIndex].ChangeTick;
        if (!flag3)
        {
          wd.Buffer.AddInt4(entity2.RawId - num1);
          wd.Buffer.Add(1, flag2 ? 1U : 0U);
        }
        if (!flag2)
        {
          if (!flag3)
          {
            pkt.AddEntity(new ObjectRecord(entity2.RawId, entity2.WorldIndex, false, -1L, simTick, -1));
            ++numOfWrittenObjects;
          }
          flag1 = true;
          index1 = n;
          n = entity1.C[n].N;
          if (n != -1)
          {
            if (!flag3)
              num1 = entity2.RawId;
            entity2 = objects[idToWorldIndex[entity1.C[n].Index]];
            wd.LastNode = entity2.GetClientNodeToWrite(clientIndex);
            continue;
          }
          if (!flag3)
            num1 = entity2.RawId;
          entity2 = (Entity) null;
          continue;
        }
      }
      else
        wd.Buffer.AddInt4(entity2.RawId - num1);
      wd.IsInputSource = entity2._inputSourceIndex == wd.Client;
      int snapshotTick1 = entity2.K[wd.Client].SnapshotTick;
      bool writeDelta = snapshotTick1 != -1 & snapshotTick1 == wd.ClientDCTick && wd.SnapshotIndex != -1;
      flag1 = entity2.WP(ref wd, writeDelta);
      pkt.AddEntity(new ObjectRecord(entity2.RawId, entity2.WorldIndex, false, wd.WrittenRangeEnd, simTick, -1));
      ++numOfWrittenObjects;
      if (!flag1)
      {
        int rawId = entity2.RawId;
        break;
      }
      index1 = n;
      n = entity1.C[n].N;
      if (n != -1)
      {
        num1 = entity2.RawId;
        entity2 = objects[idToWorldIndex[entity1.C[n].Index]];
        wd.LastNode = entity2.GetClientNodeToWrite(clientIndex);
      }
      else
      {
        num1 = entity2.RawId;
        entity2 = (Entity) null;
      }
    }
    wd.Buffer.Insert(writePos, 20, numOfWrittenObjects);
    int num2 = flag1 ? 1 : 0;
    long i2 = entity1.C[index1].I;
    snapshotTick = num2 == 1 ? snapshotTick : -1;
    pkt.AddEntity(new ObjectRecord(imGroup.Index, -1, true, i2, simTick, -1));
    return num2 == 1;
  }
}
